home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1993…ch: Other People's Memory / ADC Developer CD (1993-03) (''Other People's Memory'')_iso / Dev.CD Mar 93.iso / Technical Documentation / Sample Code / DTS.Lib & Samples / DTS.Lib / ListControl.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-10-22  |  33.9 KB  |  1,335 lines  |  [TEXT/MPS ]

  1. /*
  2. ** Apple Macintosh Developer Technical Support
  3. **
  4. ** Program:         listcontrol.c
  5. ** Written by:      Eric Soldan
  6. **
  7. ** Copyright © 1991 Apple Computer, Inc.
  8. ** All rights reserved.
  9. */
  10.  
  11. /*
  12. **
  13. ** To create a List control, you only need a single call.  For example:
  14. **
  15. **    list = CLNew(rViewCtl,            Resource ID of view control for List control.
  16. **                 &viewRect,            View rect of list.
  17. **                 numRows,            Number of rows to create List with.
  18. **                 numCols,            Number of columns to create List with.
  19. **                 cellHeight,
  20. **                 cellWidth,
  21. **                 theLProc,            Custom List procedure resource ID.
  22. **                 window,            Window to hold List control.
  23. **                 clHScroll | blBrdr | clActive        Horizontal scrollbar, active List.
  24. **    );
  25. **
  26.  
  27. ** If the CLNew call succeeds, you then have a List control in your
  28. ** window.  It will be automatically disposed of when you close the window.
  29. ** If you don't waht this to happen, then you can detach it from the
  30. ** view control which owns it.  To do this, you would to the following:
  31. **
  32. **  viewCtl = CLViewFromList(theListHndl);
  33. **  if (viewCtl) SetCRefCon(viewCtl, nil);
  34. **
  35. ** The view control keeps a reference to the List record in the refCon.
  36. ** If the refCon is cleared, then the view control does nothing.  So, all that
  37. ** is needed to detach a List record from a view control is to set the
  38. ** view control's refCon nil.  Now if you close the window, you will still
  39. ** have the List record.
  40. **
  41. **
  42. ** To remove a List control completely from a window, just dispose of the view
  43. ** control that holds the List record.  To do this, just do something like the below:
  44. **
  45. **  DisposeControl(CLViewFromList(theListHndl));
  46. **
  47. ** This completely disposes of the List control.
  48. **
  49. **
  50. ** Events for the List record are handled nearly automatically.  Just make the
  51. ** following call:
  52. **
  53. **  CLClick(window, eventPtr, &action);
  54. **
  55. ** If the event was handled, true is returned.  If the event is false, then the
  56. ** event doesn't belong to a List control, and further processing of the event
  57. ** should be done.
  58. */
  59.  
  60.  
  61.  
  62. /*****************************************************************************/
  63.  
  64.  
  65.  
  66. #ifndef __CONTROLS__
  67. #include <Controls.h>
  68. #endif
  69.  
  70. #ifndef __ERRORS__
  71. #include <Errors.h>
  72. #endif
  73.  
  74. #ifndef __LISTCONTROL__
  75. #include "ListControl.h"
  76. #endif
  77.  
  78. #ifndef __MEMORY__
  79. #include <Memory.h>
  80. #endif
  81.  
  82. #ifndef __PACKAGES__
  83. #include <Packages.h>
  84. #endif
  85.  
  86. #ifndef __RESOURCES__
  87. #include <Resources.h>
  88. #endif
  89.  
  90. #ifndef __UTILITIES__
  91. #include "Utilities.h"
  92. #endif
  93.  
  94.  
  95.  
  96. /*****************************************************************************/
  97.  
  98.  
  99.  
  100. #define kListPosTextLen 32
  101. #define kPrevSel        16
  102.  
  103. typedef struct cdefRsrcJMP {
  104.     long    moveInst;
  105.     long    jsrInst;
  106.     short    jmpInst;
  107.     long    jmpAddress;
  108. } cdefRsrcJMP;
  109. typedef cdefRsrcJMP *cdefRsrcJMPPtr, **cdefRsrcJMPHndl;
  110.  
  111.  
  112.  
  113. /*****************************************************************************/
  114.  
  115.  
  116.  
  117. static long            lastKeyTime;
  118. static char            listPosText[kListPosTextLen];
  119. static short        listPosTextLen;
  120. static short        gViewID;
  121.  
  122. static void            CLBorderDraw(ListHandle listHndl);
  123. static pascal long    CLCtl(short varCode, ControlHandle ctl, short msg, long parm);
  124. static pascal short    MyIntlCompare(Ptr aPtr, Ptr bPtr, short aLen, short bLen);
  125.  
  126. static void                dummyCLActivate(Boolean active, ListHandle listHndl);
  127. static Boolean            dummyCLClick(WindowPtr window, EventRecord *event, short *action);
  128. static ControlHandle    dummyCLCtlHit(void);
  129. static Boolean            dummyCLEvent(WindowPtr window, EventRecord *event, short *action);
  130. static ListHandle        dummyCLFindActive(WindowPtr window);
  131. static Boolean            dummyCLFindCtl(WindowPtr window, EventRecord *event, ListHandle *listHndl, ControlHandle *ctlHit);
  132. static ListHandle        dummyCLFromScroll(ControlHandle scrollCtl, ControlHandle *retCtl);
  133. static short            dummyCLInsert(ListHandle listHndl, char *data, short dataLen, short row, short col);
  134. static Boolean            dummyCLKey(WindowPtr window, EventRecord *event);
  135. static ControlHandle    dummyCLNext(WindowPtr window, ListHandle *listHndl, ControlHandle ctl);
  136. static void                dummyCLPrint(RgnHandle clipRgn, ListHandle listHndl, short *row, short *col, short leftEdge, Rect *drawRct);
  137. static short            dummyCLRowOrColSearch(ListHandle listHndl, char *data, short dataLen, short row, short col);
  138. static void                dummyCLUpdate(RgnHandle clipRgn, ListHandle list);
  139. static ControlHandle    dummyCLViewFromList(ListHandle listHndl);
  140. static void                dummyCLWindActivate(WindowPtr window);
  141.  
  142. CLActivateProcPtr        gclActivate       = dummyCLActivate;
  143. CLClickProcPtr            gclClick          = dummyCLClick;
  144. CLCtlHitProcPtr            gclCtlHit         = dummyCLCtlHit;
  145. CLEventProcPtr            gclEvent          = dummyCLEvent;
  146. CLFindActiveProcPtr        gclFindActive     = dummyCLFindActive;
  147. CLFindCtlProcPtr        gclFindCtl        = dummyCLFindCtl;
  148. CLFromScrollProcPtr        gclFromScroll     = dummyCLFromScroll;
  149. CLInsertProcPtr            gclInsert         = dummyCLInsert;
  150. CLKeyProcPtr            gclKey            = dummyCLKey;
  151. CLNextProcPtr            gclNext           = dummyCLNext;
  152. CLPrintProcPtr            gclPrint          = dummyCLPrint;
  153. CLRowOrColSearchProcPtr    gclRowOrColSearch = dummyCLRowOrColSearch;
  154. CLUpdateProcPtr            gclUpdate         = dummyCLUpdate;
  155. CLViewFromListProcPtr    gclViewFromList   = dummyCLViewFromList;
  156. CLWindActivateProcPtr    gclWindActivate   = dummyCLWindActivate;
  157.  
  158. extern short    gPrintPage;        /* Non-zero means we are printing. */
  159.  
  160.  
  161.  
  162. /*****************************************************************************/
  163.  
  164.  
  165.  
  166. static ListHandle        gFoundLHndl;
  167.     /* Global value used to return info from the List control proc. */
  168.  
  169. static ControlHandle    gFoundViewCtl;
  170.     /* Global value used to return info from the List control proc. */
  171.  
  172.  
  173.  
  174. /*****************************************************************************/
  175. /*****************************************************************************/
  176. /*****************************************************************************/
  177.  
  178.  
  179.  
  180. /* Instead of calling the functions directly, you can reference the global
  181. ** proc pointers that reference the functions.  This keeps everything from
  182. ** being linked in.  The default global proc pointers point to dummy functions
  183. ** that behave as if there aren't any list controls.  The calls can still be
  184. ** made, yet the runtime behavior is such that it will operate as if there
  185. ** no instances of the List control.  This allows intermediate code to access
  186. ** the functions or not without automatically linking in all sorts of stuff
  187. ** into the application that isn't desired.  To change the global proc pointers
  188. ** so that they point to the actual functions, just call CLInitialize() once
  189. ** in the beginning of the application.  If CLInitialize() is referenced, it will
  190. ** get linked in.  In turn, everything that it references directly or indirectly
  191. ** will get linked in. */
  192.  
  193. #pragma segment Controls
  194. void    CLInitialize(void)
  195. {
  196.     if (gclActivate != CLActivate) {
  197.         gclActivate       = CLActivate;
  198.         gclClick          = CLClick;
  199.         gclCtlHit         = CLCtlHit;
  200.         gclEvent          = CLEvent;
  201.         gclFindActive     = CLFindActive;
  202.         gclFindCtl        = CLFindCtl;
  203.         gclFromScroll     = CLFromScroll;
  204.         gclInsert         = CLInsert;
  205.         gclKey            = CLKey;
  206.         gclNext           = CLNext;
  207.         gclPrint          = CLPrint;
  208.         gclRowOrColSearch = CLRowOrColSearch;
  209.         gclUpdate         = CLUpdate;
  210.         gclViewFromList   = CLViewFromList;
  211.         gclWindActivate   = CLWindActivate;
  212.     }
  213. }
  214.  
  215.  
  216.  
  217. /*****************************************************************************/
  218.  
  219.  
  220.  
  221. /* Activate this List record.  Activation is not done by calling LActivate().
  222. ** The active control is indicated by the 2-pixel thick border around the
  223. ** List control.  This allows all List controls in a window to display which
  224. ** cells are selected.  This behavior can be overridden by calling LActivate()
  225. ** on the List record for List controls.
  226. ** Human interface dictates that only at most a single List control has this
  227. ** active border.  For this reason, this function scans for other List
  228. ** controls in the window and removes the border from any other that it finds. */
  229.  
  230. #pragma segment Controls
  231. void    CLActivate(Boolean active, ListHandle listHndl)
  232. {
  233.     WindowPtr        window, oldPort;
  234.     ControlHandle    viewCtl;
  235.     short            oldDisplay, newDisplay;
  236.     ListHandle        list;
  237.     CLDataHndl        listData;
  238.  
  239.     if (listHndl) {
  240.         window = (WindowPtr)(*listHndl)->port;
  241.         for (viewCtl = nil;;) {
  242.             viewCtl = CLNext(window, &list, viewCtl);
  243.             if (!viewCtl) break;
  244.             listData   = (CLDataHndl)(*viewCtl)->contrlData;
  245.             oldDisplay = (*listData)->mode;
  246.             newDisplay = (oldDisplay & (0xFFFF - clActive));
  247.             if (active)
  248.                 if (list == listHndl)
  249.                     newDisplay |= clActive;
  250.             if (oldDisplay != newDisplay) {
  251.                 (*listData)->mode = newDisplay;
  252.                 GetPort(&oldPort);
  253.                 SetPort(window);
  254.                 CLBorderDraw(list);
  255.                 SetPort(oldPort);
  256.             }
  257.         }
  258.     }
  259. }
  260.  
  261.  
  262.  
  263. static void    dummyCLActivate(Boolean active, ListHandle listHndl)
  264. {
  265. #pragma unused (active, listHndl)
  266. }
  267.  
  268.  
  269.  
  270. /*****************************************************************************/
  271.  
  272.  
  273.  
  274. #pragma segment Controls
  275. static void    CLBorderDraw(ListHandle listHndl)
  276. {
  277.     ControlHandle    viewCtl;
  278.     WindowPtr        oldPort, listPort;
  279.     short            displayInfo;
  280.     Rect            rct;
  281.     PenState        oldPen;
  282.     CLDataHndl        listData;
  283.  
  284.     if (listHndl) {
  285.         if (viewCtl = CLViewFromList(listHndl)) {
  286.             GetPort(&oldPort);
  287.             SetPort(listPort = (*listHndl)->port);
  288.             GetPenState(&oldPen);
  289.             PenNormal();
  290.             listData    = (CLDataHndl)(*viewCtl)->contrlData;
  291.             displayInfo = (*listData)->mode;
  292.             rct = (*listHndl)->rView;
  293.             InsetRect(&rct, -1, -1);
  294.             FrameRect(&rct);
  295.             if (displayInfo & clShowActive) {
  296.                 rct = (*listHndl)->rView;
  297.                 InsetRect(&rct, -4, -4);
  298.                 if ((*listHndl)->vScroll)
  299.                     rct.right  += 15;
  300.                 if ((*listHndl)->hScroll)
  301.                     rct.bottom += 15;
  302.                 PenSize(2, 2);
  303.                 if ((!((WindowPeek)listPort)->hilited) || (!(displayInfo & clActive)))
  304.                     PenPat((ConstPatternParam)&qd.white);
  305.                 FrameRect(&rct);
  306.             }
  307.             SetPenState(&oldPen);
  308.             SetPort(oldPort);
  309.         }
  310.     }
  311. }
  312.  
  313.  
  314.  
  315. /*****************************************************************************/
  316.  
  317.  
  318.  
  319. /* This is called when a mouseDown occurs in the content of a window.  It
  320. ** returns true if the mouseDown caused a List action to occur.  Events
  321. ** that are handled include if the user clicks on a scrollbar that is
  322. ** associated with a List control. */
  323.  
  324. #pragma segment Controls
  325. Boolean    CLClick(WindowPtr window, EventRecord *event, short *action)
  326. {
  327.     WindowPtr        oldPort;
  328.     Point            mouseLoc;
  329.     ListHandle        list;
  330.     ControlHandle    ctlHit, viewCtl;
  331.  
  332.     if (action)
  333.         *action = 0;
  334.     lastKeyTime = 0;
  335.  
  336.     GetPort(&oldPort);
  337.     if (!((WindowPeek)window)->hilited) return(false);
  338.  
  339.     SetPort(window);
  340.     mouseLoc = event->where;
  341.     GlobalToLocal(&mouseLoc);
  342.  
  343.     if (!CLFindCtl(window, event, &list, &ctlHit)) {
  344.         /* If user didn't hit a list control, then maybe they hit a scrollbar for
  345.         ** a list control... */
  346.  
  347.         if (WhichControl(mouseLoc, window, &ctlHit)) {
  348.             /* Well, they did hit a control.  Is it a scrollbar? */
  349.  
  350.             if (IsScrollBar(ctlHit)) {
  351.                 /* Well, it was a scrollbar, but is the scroller related to a list control? */
  352.  
  353.                 list = CLFromScroll(ctlHit, &viewCtl);
  354.                     /* If list is nil, then the scrollbar wasn't for a list control. */
  355.             }
  356.         }
  357.     }
  358.  
  359.     if (!list) {
  360.         SetPort(oldPort);
  361.         return(false);
  362.     }        /* Didn't hit list control or related scrollbar.  No action taken. */
  363.  
  364.  
  365.     if (CLFindActive(window) != list) {        /* If not active list control, activate it.       */
  366.         CLActivate(true, list);                /* Now is the active list control.               */
  367.         if (action)                            /* CLClick can be called again if the control  */
  368.             *action = -1;                    /* activates and operates with the same click. */
  369.     }
  370.     else {
  371.         if (LClick(mouseLoc, event->modifiers, list))
  372.             if (action)
  373.                 *action = 1;
  374.                     /* If double-click, then return that it was. */
  375.     }
  376.  
  377.     SetPort(oldPort);
  378.     return(true);
  379. }
  380.  
  381.  
  382.  
  383. static Boolean    dummyCLClick(WindowPtr window, EventRecord *event, short *action)
  384. {
  385. #pragma unused (window, event)
  386.  
  387.     if (action)
  388.         *action = 0;
  389.     return(false);
  390. }
  391.  
  392.  
  393.  
  394. /*****************************************************************************/
  395.  
  396.  
  397.  
  398. #pragma segment Controls
  399. static pascal long    CLCtl(short varCode, ControlHandle ctl, short msg, long parm)
  400. {
  401. #pragma unused (varCode)
  402.  
  403.     Rect            viewRct;
  404.     ListHandle        list;
  405.     WindowPtr        thePort;
  406.  
  407.     if (list = (ListHandle)GetCRefCon(ctl))
  408.         viewRct = (*list)->rView;
  409.     else
  410.         SetRect(&viewRct, 0, 0, 0, 0);
  411.  
  412.     switch (msg) {
  413.         case drawCntl:
  414.             GetPort(&thePort);
  415.             CLUpdate(thePort->visRgn, list);
  416.             CLBorderDraw(list);
  417.             break;
  418.  
  419.         case testCntl:
  420.             if (PtInRect(*(Point *)&parm, &viewRct)) {
  421.                 gFoundViewCtl = ctl;
  422.                 gFoundLHndl   = list;
  423.                 return(1);
  424.             }
  425.             return(0);
  426.             break;
  427.  
  428.         case calcCRgns:
  429.         case calcCntlRgn:
  430.             if (msg == calcCRgns)
  431.                 parm &= 0x00FFFFFF;
  432.             RectRgn((RgnHandle)parm, &viewRct);
  433.             break;
  434.  
  435.         case initCntl:
  436.             break;
  437.  
  438.         case dispCntl:
  439.             if (list) {
  440.                 LDispose(list);
  441.                 DisposeHandle((Handle)(*ctl)->contrlData);
  442.             }
  443.             break;
  444.  
  445.         case posCntl:
  446.             break;
  447.  
  448.         case thumbCntl:
  449.             break;
  450.  
  451.         case dragCntl:
  452.             break;
  453.  
  454.         case autoTrack:
  455.             break;
  456.     }
  457.  
  458.     return(0);
  459. }
  460.  
  461.  
  462.  
  463. /*****************************************************************************/
  464.  
  465.  
  466.  
  467. /* The List control that was hit by calling FindControl is saved in a
  468. ** global variable, since the CDEF has no way of returning what kind it was.
  469. ** To determine that it was a List control that was hit, first call this
  470. ** function.  The first call returns the old value in the global variable,
  471. ** plus it resets the global to nil.  Then call FindControl(), and then
  472. ** call this function again.  If it returns nil, then a List control
  473. ** wasn't hit.  If it returns non-nil, then it was a List control that
  474. ** was hit, and specifically the one returned. */
  475.  
  476. #pragma segment Controls
  477. ControlHandle    CLCtlHit(void)
  478. {
  479.     ControlHandle    ctl;
  480.  
  481.     ctl = gFoundViewCtl;
  482.     gFoundViewCtl = nil;
  483.     return(ctl);
  484. }
  485.  
  486.  
  487.  
  488. static ControlHandle    dummyCLCtlHit(void)
  489. {
  490.     return(nil);
  491. }
  492.  
  493.  
  494.  
  495. /*****************************************************************************/
  496.  
  497.  
  498.  
  499. /* Handle the event if it applies to the active List control.  If some
  500. ** action occured due to the event, return true. */
  501.  
  502. #pragma segment Controls
  503. Boolean    CLEvent(WindowPtr window, EventRecord *event, short *action)
  504. {
  505.     WindowPtr    clickWindow;
  506.     short        actn;
  507.  
  508.     if (action)
  509.         *action = 0;
  510.  
  511.     switch(event->what) {
  512.  
  513.         case mouseDown:
  514.             if (FindWindow(event->where, &clickWindow) == inContent)
  515.                 if (window == clickWindow)
  516.                     if (((WindowPeek)window)->hilited) return(CLClick(window, event, action));
  517.             break;
  518.  
  519.         case autoKey:
  520.         case keyDown:
  521.             if (!(event->modifiers & cmdKey)) {
  522.                 actn = CLKey(window, event);
  523.                 if (action)
  524.                     *action = actn;
  525.                 if (actn) return(true);
  526.             }
  527.             break;
  528.     }
  529.  
  530.     return(false);
  531. }
  532.  
  533.  
  534.  
  535. static Boolean    dummyCLEvent(WindowPtr window, EventRecord *event, short *action)
  536. {
  537. #pragma unused (window, event)
  538.  
  539.     if (action)
  540.         *action = 0;
  541.     return(false);
  542. }
  543.  
  544.  
  545.  
  546. /*****************************************************************************/
  547.  
  548.  
  549.  
  550. /* Returns the active List control, if any.  Unlike the TextEdit control, passing
  551. ** in nil doesn't return the currently active control independent of window.  The
  552. ** only reason that the TextEdit control returns the "globally active" control is
  553. ** so that the TEIdle procedure can do its thing.  The List control doesn't have
  554. ** such a thing, so there is no purpose for a "globally active" control.  If the
  555. ** window pointer passed in is nil (requesting the "globally active" List control),
  556. ** we just return nil, indicating that there isn't one. */
  557.  
  558. #pragma segment Controls
  559. ListHandle    CLFindActive(WindowPtr window)
  560. {
  561.     ControlHandle    viewCtl;
  562.     ListHandle        list;
  563.     short            display;
  564.     CLDataHndl        listData;
  565.  
  566.     if (!window) return(nil);
  567.  
  568.     for (viewCtl = nil;;) {
  569.         viewCtl = CLNext(window, &list, viewCtl);
  570.         if (!viewCtl) break;
  571.         listData = (CLDataHndl)(*viewCtl)->contrlData;
  572.         display  = (*listData)->mode;
  573.         if (display & clActive) break;
  574.     }
  575.     return(list);
  576. }
  577.  
  578.  
  579.  
  580. static ListHandle    dummyCLFindActive(WindowPtr window)
  581. {
  582. #pragma unused (window)
  583.     return(nil);
  584. }
  585.  
  586.  
  587.  
  588. /*****************************************************************************/
  589.  
  590.  
  591.  
  592. /* This determines if a List control was clicked on directly.  This does
  593. ** not determine if a related scrollbar was clicked on.  If a List
  594. ** control was clicked on, then true is returned, as well as the List
  595. ** handle and the handle to the view control. */
  596.  
  597. #pragma segment Controls
  598. Boolean    CLFindCtl(WindowPtr window, EventRecord *event, ListHandle *listHndl, ControlHandle *ctlHit)
  599. {
  600.     WindowPtr        oldPort;
  601.     Point            mouseLoc;
  602.  
  603.     *listHndl = nil;
  604.  
  605.     if (window) {
  606.         GetPort(&oldPort);
  607.         SetPort(window);
  608.         mouseLoc = event->where;
  609.         GlobalToLocal(&mouseLoc);
  610.         SetPort(oldPort);
  611.  
  612.         gFoundLHndl = nil;
  613.         FindControl(mouseLoc, window, ctlHit);
  614.         if (*listHndl = gFoundLHndl) return(true);
  615.     }
  616.  
  617.     *ctlHit = nil;
  618.     return(false);
  619. }
  620.  
  621.  
  622.  
  623. static Boolean    dummyCLFindCtl(WindowPtr window, EventRecord *event, ListHandle *listHndl, ControlHandle *ctlHit)
  624. {
  625. #pragma unused (window, event)
  626.     *listHndl = nil;
  627.     *ctlHit   = nil;
  628.     return(false);
  629. }
  630.  
  631.  
  632.  
  633. /*****************************************************************************/
  634.  
  635.  
  636.  
  637. /* Find the List record that is related to the indicated scrollbar. */
  638.  
  639. #pragma segment Controls
  640. ListHandle    CLFromScroll(ControlHandle scrollCtl, ControlHandle *retCtl)
  641. {
  642.     WindowPtr        window;
  643.     ControlHandle    viewCtl;
  644.     ListHandle        list;
  645.  
  646.     *retCtl = nil;
  647.     if (!IsScrollBar(scrollCtl)) return(nil);
  648.  
  649.     window = (*scrollCtl)->contrlOwner;
  650.  
  651.     for (*retCtl = viewCtl = nil;;) {
  652.         viewCtl = CLNext(window, &list, viewCtl);
  653.         if (!viewCtl) return(nil);
  654.         list = (ListHandle)GetCRefCon(viewCtl);
  655.         if (
  656.             ((*list)->vScroll == scrollCtl) || 
  657.             ((*list)->hScroll == scrollCtl)
  658.         ) {
  659.             *retCtl = viewCtl;
  660.             return(list);
  661.         }
  662.     }
  663. }
  664.  
  665.  
  666.  
  667. static ListHandle    dummyCLFromScroll(ControlHandle scrollCtl, ControlHandle *retCtl)
  668. {
  669. #pragma unused (scrollCtl)
  670.     *retCtl = nil;
  671.     return(nil);
  672. }
  673.  
  674.  
  675.  
  676. /*****************************************************************************/
  677.  
  678.  
  679.  
  680. /* Insert a cell alphabetically into the list.  Whichever parameter is passed in
  681. ** as -1, either row or column, that is the dimension that is determined. */
  682.  
  683. #pragma segment Controls
  684. short    CLInsert(ListHandle listHndl, char *data, short dataLen, short row, short col)
  685. {
  686.     short    loc;
  687.     Point    cell;
  688.  
  689.     if (!listHndl) return(-1);
  690.  
  691.     loc = CLRowOrColSearch(listHndl, data, dataLen, row, col);
  692.  
  693.     if (row == -1) {
  694.         LAddRow(1, cell.v = loc, listHndl);
  695.         cell.h = col;
  696.     }
  697.     else {
  698.         LAddColumn(1, cell.h = loc, listHndl);
  699.         cell.v = row;
  700.     }
  701.  
  702.     LSetCell(data, dataLen, cell, listHndl);
  703.     return(loc);
  704. }
  705.  
  706.  
  707.  
  708. static short    dummyCLInsert(ListHandle listHndl, char *data, short dataLen, short row, short col)
  709. {
  710. #pragma unused (listHndl, data, dataLen, row, col)
  711.     return(0);
  712. }
  713.  
  714.  
  715.  
  716. /*****************************************************************************/
  717.  
  718.  
  719.  
  720. /* See if the keypress event applies to the List control, and if it does,
  721. ** handle it and return true. */
  722.  
  723. #pragma segment Controls
  724. Boolean    CLKey(WindowPtr window, EventRecord *event)
  725. {
  726.     ListHandle        list;
  727.     ControlHandle    listCtl;
  728.     short            key, mode;
  729.     Point            cell, dcell;
  730.     Rect            bnds, visCells;
  731.     CLDataHndl        listData;
  732.  
  733.     if (list = CLFindActive(window)) {
  734.  
  735.         listCtl  = CLViewFromList(list);
  736.         listData = (CLDataHndl)(*listCtl)->contrlData;
  737.         mode     = (*listData)->mode;
  738.         if (!(mode & clKeyPos)) return(false);
  739.  
  740.         bnds = (*list)->dataBounds;
  741.         if (bnds.top == bnds.bottom) return(true);
  742.         if (bnds.left == bnds.right) return(true);
  743.             /* The list is empty, so whatever was typed has been "handled". */
  744.  
  745.         cell.h = bnds.left;
  746.         cell.v = bnds.top;
  747.         key = event->message & charCodeMask;
  748.  
  749.         if ((key >= chLeft) && (key <= chDown)) {
  750.             if (LGetSelect(true, &cell, list))
  751.                 key -= kPrevSel;
  752.             switch (key) {
  753.                 case chLeft - kPrevSel:
  754.                     if (cell.h > bnds.left)
  755.                         --cell.h;
  756.                     break;
  757.                 case chRight - kPrevSel:
  758.                     if (cell.h < bnds.right - 1)
  759.                         ++cell.h;
  760.                     break;
  761.                 case chUp - kPrevSel:
  762.                     if (cell.v > bnds.top)
  763.                         --cell.v;
  764.                     break;
  765.                 case chDown - kPrevSel:
  766.                     if (cell.v < bnds.bottom - 1)
  767.                         ++cell.v;
  768.                     break;
  769.                 case chLeft:
  770.                     if (bnds.top < bnds.bottom - 1) return(true);
  771.                         /* With no previous selection, if the list has
  772.                         ** more than one row, we don't know which to use.
  773.                         ** Selection is indeterminate, so do nothing. */
  774.                     cell.v = bnds.top;
  775.                     cell.h = bnds.right - 1;
  776.                     break;
  777.                 case chRight:
  778.                     if (bnds.top < bnds.bottom - 1) return(true);
  779.                         /* With no previous selection, if the list has
  780.                         ** more than one row, we don't know which to use.
  781.                         ** Selection is indeterminate, so do nothing. */
  782.                     cell.v = bnds.top;
  783.                     cell.h = bnds.left;
  784.                     break;
  785.                 case chUp:
  786.                     if (bnds.left < bnds.right - 1) return(true);
  787.                         /* With no previous selection, if the list has
  788.                         ** more than one row, we don't know which to use.
  789.                         ** Selection is indeterminate, so do nothing. */
  790.                     cell.h = bnds.left;
  791.                     cell.v = bnds.bottom - 1;
  792.                     break;
  793.                 case chDown:
  794.                     if (bnds.left < bnds.right - 1) return(true);
  795.                         /* With no previous selection, if the list has
  796.                         ** more than one row, we don't know which to use.
  797.                         ** Selection is indeterminate, so do nothing. */
  798.                     cell.h = bnds.left;
  799.                     cell.v = bnds.top;
  800.                     break;
  801.             }
  802.         }
  803.         else {
  804.             if ((bnds.right - bnds.left > 1) && (bnds.bottom - bnds.top > 1)) return(true);
  805.             if (lastKeyTime + 45 < TickCount()) {        /* Reset character collection. */
  806.                 listPosTextLen = 0;
  807.                 lastKeyTime = TickCount();
  808.             }
  809.             if (listPosTextLen < kListPosTextLen)
  810.                 listPosText[listPosTextLen++] = key;
  811.             LSearch(listPosText, listPosTextLen, MyIntlCompare, &cell, list);
  812.                 /* Find the cell closest to the query test. */
  813.             if (cell.h >= bnds.right)
  814.                 cell.h = bnds.right - 1;
  815.             if (cell.v >= bnds.bottom)
  816.                 cell.v = bnds.bottom - 1;
  817.         }
  818.  
  819.         LSetSelect(true, cell, list);        /* Select cell that is closest. */
  820.         dcell.h = bnds.left;
  821.         dcell.v = bnds.top;
  822.         for (;;) {                /* Deselect old cells. */
  823.             if (!LGetSelect(true, &dcell, list)) break;
  824.             if ((dcell.h == cell.h) && (dcell.v == cell.v)) {
  825.                 if (++dcell.h >= bnds.right) {
  826.                     dcell.h = bnds.left;
  827.                     ++dcell.v;
  828.                 }
  829.             }
  830.             else LSetSelect(false, dcell, list);
  831.         }
  832.  
  833.         visCells = (*list)->visible;
  834.         if (PtInRect(cell, &visCells)) return(true);        /* Already in view. */
  835.  
  836.         if (
  837.             (key != (chDown - kPrevSel)) || 
  838.             (cell.v < visCells.top) || 
  839.             (cell.h < visCells.left)
  840.         ) {
  841.             LAutoScroll(list);        /* Let List manager scroll into view. */
  842.             return(true);            /* It handles these cases correctly.  */
  843.         }
  844.  
  845.         LScroll(cell.h - --visCells.right, cell.v - --visCells.bottom, list);
  846.             /* Scroll into view the way we want it done. */
  847.  
  848.         return(true);
  849.     }
  850.  
  851.     return(false);
  852. }
  853.  
  854.  
  855.  
  856. static Boolean    dummyCLKey(WindowPtr window, EventRecord *event)
  857. {
  858. #pragma unused (window, event)
  859.     return(false);
  860. }
  861.  
  862.  
  863.  
  864. /*****************************************************************************/
  865.  
  866.  
  867.  
  868. /* Create a new List control.  See the comments at the beginning of this
  869. ** file for more information.  Note that this function doesn't get a dummy,
  870. ** as you really mean to have this code if you call it.  It calls CLInitialize(),
  871. ** just to make sure that the proc pointers are set to point to the real function,
  872. ** instead of the dummy functions.  By having CLNew() call CLInitialize(), the
  873. ** application won't have to worry about calling CLInitialize().  By the time that
  874. ** the application is using a List control, the proc pointers will be initialized. */
  875.  
  876. #pragma segment Controls
  877. ListHandle    CLNew(short viewID, Rect *vRect, short numRows, short numCols, short cellHeight,
  878.                   short cellWidth, short theLProc, WindowPtr window, short mode)
  879. {
  880.     WindowPtr        oldPort;
  881.     Rect            viewRect, dataBnds;
  882.     Point            cellSize;
  883.     ListHandle        list;
  884.     Boolean            err;
  885.     ControlHandle    viewCtl;
  886.     cdefRsrcJMPHndl    cdefRsrc;
  887.     Boolean            drawIt, hScroll, vScroll;
  888.     CLDataHndl        listData;
  889.  
  890.     CLInitialize();            /* Make sure that this code gets linked in, in case the application
  891.                             ** is using the proc pointers.  CtlHandler.c uses the proc pointers
  892.                             ** so that if the application doesn't use List controls, this code
  893.                             ** won't get linked in. */
  894.  
  895.     gViewID = viewID;        /* Keep viewID that was passed in. */
  896.  
  897.     GetPort(&oldPort);
  898.     SetPort(window);
  899.  
  900.     viewRect = *vRect;
  901.     dataBnds.top = dataBnds.left = 0;
  902.     dataBnds.right  = numCols;
  903.     dataBnds.bottom = numRows;
  904.  
  905.     cellSize.h = cellWidth;
  906.     cellSize.v = cellHeight;
  907.  
  908.     drawIt  = (mode & clDrawIt) ? 1 : 0;
  909.     hScroll = (mode & clHScroll) ? 1 : 0;
  910.     vScroll = (mode & clVScroll) ? 1 : 0;
  911.     list = LNew(&viewRect, &dataBnds, cellSize, theLProc, window, drawIt, false, hScroll, vScroll);
  912.  
  913.     err = false;
  914.     if (list) {        /* If we were able to create the List record... */
  915.  
  916.         cdefRsrc = (cdefRsrcJMPHndl)GetResource('CDEF', viewID);
  917.         (*cdefRsrc)->jmpAddress = (long)CLCtl;
  918.         FlushInstructionCache();
  919.             /* Make sure that instruction caches don't kill us. */
  920.  
  921.         viewCtl = NewControl(window, &viewRect, nil, false, 0, 0, 0,
  922.                              viewID * 16, (long)list);
  923.             /* Use our custom view cdef.  It's wierd, but it's small. */
  924.             /* We have to create the control initially invisible because we haven't */
  925.             /* initialized all of the data needed by the update procedure.  If it   */
  926.             /* is created visible, the update procedure will be immediately called, */
  927.             /* and this would be very bad to do until all of the data is there.     */
  928.  
  929.         mode &= (0xFFFF - clDrawIt);
  930.  
  931.         if (!viewCtl)
  932.             err = true;
  933.         else {
  934.             (*viewCtl)->contrlData = nil;
  935.             if (listData = (CLDataHndl)NewHandle(sizeof(CLDataRec))) {
  936.                 (*listData)->mode   = mode;
  937.                 (*listData)->txFont = window->txFont;
  938.                 (*listData)->txFace = window->txFace;
  939.                 (*listData)->txMode = window->txMode;
  940.                 (*listData)->txSize = window->txSize;
  941.                 (*viewCtl)->contrlData = (Handle)listData;
  942.                 ShowControl(viewCtl);
  943.                     /* Now that the data is initialized, we can show the control. */
  944.             }
  945.             else err = true;
  946.  
  947.             if (mode & clActive)
  948.                 CLActivate(true, list);
  949.         }
  950.     }
  951.     else err = true;
  952.  
  953.     SetPort(oldPort);
  954.  
  955.     if (err) {        /* Oops.  Somebody wasn't happy. */
  956.         if (viewCtl)
  957.             DisposeControl(viewCtl);
  958.                 /* This also disposes of the List handle! */
  959.         else
  960.             if (list)
  961.                 LDispose(list);
  962.                     /* We have to dispose of the List handle ourselves if
  963.                     ** creating the view control failed. */
  964.  
  965.         list = nil;        /* Return that there is no List control. */
  966.     }
  967.  
  968.     return(list);
  969. }
  970.  
  971.  
  972.  
  973. /*****************************************************************************/
  974.  
  975.  
  976.  
  977. /* Get the next List control in the window.  You pass it a control handle
  978. ** for the view control, or nil to start at the beginning of the window.
  979. ** It returns both a List handle and the view control handle for that
  980. ** List record.  If none is found, nil is returned.  This allows you to
  981. ** repeatedly call this function and walk through all the List controls
  982. ** in a window. */
  983.  
  984. #pragma segment Controls
  985. ControlHandle    CLNext(WindowPtr window, ListHandle *listHndl, ControlHandle ctl)
  986. {
  987.     short    defProcID;
  988.     ResType    defProcType;
  989.     Str255    defProcName;
  990.  
  991.     *listHndl = nil;
  992.     if (!window) return(nil);
  993.  
  994.     if (!gViewID) return(nil);
  995.  
  996.     if (!ctl)
  997.         ctl = ((WindowPeek)window)->controlList;
  998.     else
  999.         ctl = (*ctl)->nextControl;
  1000.  
  1001.     while (ctl) {
  1002.         defProcID = !gViewID;
  1003.         GetResInfo((*ctl)->contrlDefProc, &defProcID, &defProcType, defProcName);
  1004.         if (defProcID == gViewID) {
  1005.             *listHndl = (ListHandle)GetCRefCon(ctl);
  1006.             break;
  1007.         }
  1008.         ctl = (*ctl)->nextControl;
  1009.     }
  1010.  
  1011.     return(ctl);
  1012. }
  1013.  
  1014.  
  1015.  
  1016. static ControlHandle    dummyCLNext(WindowPtr window, ListHandle *listHndl, ControlHandle ctl)
  1017. {
  1018. #pragma unused (window, ctl)
  1019.     *listHndl = nil;
  1020.     return(nil);
  1021. }
  1022.  
  1023.  
  1024.  
  1025. /*****************************************************************************/
  1026.  
  1027.  
  1028.  
  1029. /* From the starting for or column, print as many cells as will fit into the
  1030. ** designated rect.  Pass in a starting row and column, and they will be
  1031. ** adjusted to indicate the first cell that didn't fit into the rect.  If all
  1032. ** remaining cells were printed, the row is returned as -1.  The bottom of the
  1033. ** rect to print in is also adjusted to indicate where the actual cut-off
  1034. ** point was. */
  1035.  
  1036. #pragma segment Controls
  1037. void    CLPrint(RgnHandle clipRgn, ListHandle listHndl, short *row, short *col,
  1038.                 short leftEdge, Rect *drawRct)
  1039. {
  1040.     Rect        dataBnds, keepView, keepVis;
  1041.     Point        csize;
  1042.     short        h, v;
  1043.     RgnHandle    rgn;
  1044.  
  1045.     if (!listHndl) return;
  1046.  
  1047.     dataBnds = (*listHndl)->dataBounds;
  1048.     if ((*col < dataBnds.left) || (*col >= dataBnds.right))
  1049.         *col = leftEdge;
  1050.     if (*row < dataBnds.top)
  1051.         *row = dataBnds.top;
  1052.     if (*row >= dataBnds.bottom) {
  1053.         *row = -1;
  1054.         return;
  1055.     }
  1056.  
  1057.     keepView = (*listHndl)->rView;
  1058.     csize    = (*listHndl)->cellSize;
  1059.     keepVis  = (*listHndl)->visible;
  1060.  
  1061.     h = (drawRct->right - drawRct->left) / csize.h;
  1062.     if (!h)
  1063.         ++h;
  1064.     v = (drawRct->bottom - drawRct->top) / csize.v;
  1065.     if (!v)
  1066.         ++v;
  1067.  
  1068.     if (*col + h > dataBnds.right)
  1069.         h = dataBnds.right  - *col;
  1070.     if (*row + v > dataBnds.bottom)
  1071.         v = dataBnds.bottom - *row;
  1072.  
  1073.     drawRct->bottom = drawRct->top + v * csize.v;
  1074.  
  1075.     (*listHndl)->rView = *drawRct;
  1076.     (*listHndl)->visible.right  = ((*listHndl)->visible.left = *col) + h;
  1077.     (*listHndl)->visible.bottom = ((*listHndl)->visible.top  = *row) + v;
  1078.  
  1079.     if (!(rgn = clipRgn)) {
  1080.         rgn = NewRgn();
  1081.         RectRgn(rgn, drawRct);
  1082.     }
  1083.     CLUpdate(rgn, listHndl);
  1084.     if (!clipRgn)
  1085.         DisposeRgn(rgn);
  1086.  
  1087.     (*listHndl)->rView   = keepView;
  1088.     (*listHndl)->visible = keepVis;
  1089.  
  1090.     *col += h;
  1091.     if (*col >= dataBnds.right) {
  1092.         *col = leftEdge;
  1093.         *row += v;
  1094.         if (*row >= dataBnds.bottom)
  1095.             *row = -1;
  1096.     }
  1097. }
  1098.  
  1099.  
  1100.  
  1101. static void    dummyCLPrint(RgnHandle clipRgn, ListHandle listHndl, short *row, short *col,
  1102.                          short leftEdge, Rect *drawRct)
  1103. {
  1104. #pragma unused (clipRgn, listHndl, col, leftEdge, drawRct)
  1105.     *row = -1;
  1106. }
  1107.  
  1108.  
  1109.  
  1110. /*****************************************************************************/
  1111.  
  1112.  
  1113.  
  1114. /* Find the location in the list where the data would belong if inserted.  The row
  1115. ** and column are passed in.  If either is -1, that is the dimension that will be
  1116. ** determined and returned. */
  1117.  
  1118. #pragma segment Controls
  1119. short    CLRowOrColSearch(ListHandle listHndl, char *data, short dataLen, short row, short col)
  1120. {
  1121.     Rect    dataBnds;
  1122.     short    numCells, baseCell, iter, pow, loc, cdataLen;
  1123.     Point    cell;
  1124.     Str255    cdata;
  1125.  
  1126.     if (!listHndl) return(-1);
  1127.  
  1128.     dataBnds = (*listHndl)->dataBounds;
  1129.     if (row == -1)
  1130.         numCells = dataBnds.bottom - (baseCell = dataBnds.top);
  1131.     else
  1132.         numCells = dataBnds.right  - (baseCell = dataBnds.left);
  1133.             /* Get some reference info on the size/start of the list. */
  1134.  
  1135.     cell.v = cell.h = 0;
  1136.     if (numCells) {
  1137.         if (row != -1)
  1138.             cell.v = row;
  1139.         if (col != -1)
  1140.             cell.h = col;
  1141.         for (pow = 1; pow < numCells; ++iter, pow <<= 1);
  1142.         pow >>= 1;        /* pow = 2^n such that pow < numCells. */
  1143.  
  1144.         for (loc = pow; pow;) {            /* Do binary search for where to insert. */
  1145.             if (loc >= numCells)
  1146.                 loc = numCells - 1;        /* Off the end is bad. */
  1147.             if (row == -1)
  1148.                 cell.v = baseCell + loc;
  1149.             else
  1150.                 cell.h = baseCell + loc;
  1151.             pow >>= 1;
  1152.  
  1153.             cdataLen = 255;
  1154.             LGetCell(cdata, &cdataLen, cell, listHndl);
  1155.                 /* Get cell data to compare against. */
  1156.  
  1157.             loc += (pow * IUMagString(data, cdata, dataLen, cdataLen));
  1158.                 /* Adjust location based on compare result. */
  1159.         }
  1160.  
  1161.         /* The binary search got us close, but not exact.  We may be off by one
  1162.         ** in either direction.  (The binary search can't position in front of
  1163.         ** the first cell in the list, for example.)  Do a linear compare from
  1164.         ** this point to find the correct cell we should insert in front of. */
  1165.  
  1166.         if (loc) --loc;        /* Start linear search one back. */
  1167.         for (;; ++loc) {
  1168.             if (row == -1)
  1169.                 cell.v = baseCell + loc;
  1170.             else
  1171.                 cell.h = baseCell + loc;
  1172.             if (loc >= numCells) break;
  1173.             cdataLen = 255;
  1174.             LGetCell(cdata, &cdataLen, cell, listHndl);
  1175.             if (IUMagString(data, cdata, dataLen, cdataLen) < 1) break;
  1176.                 /* If we are less than or equal to this cell, we have
  1177.                 ** found our insertion point, so break. */
  1178.         }
  1179.     }
  1180.  
  1181.     if (row == -1) return(cell.v);
  1182.     else           return(cell.h);
  1183. }
  1184.  
  1185.  
  1186.  
  1187. static short    dummyCLRowOrColSearch(ListHandle listHndl, char *data, short dataLen, short row, short col)
  1188. {
  1189. #pragma unused (listHndl, data, dataLen, row, col)
  1190.     return(0);
  1191. }
  1192.  
  1193.  
  1194.  
  1195. /*****************************************************************************/
  1196.  
  1197.  
  1198.  
  1199. /* Draw the List control in the correct form. */
  1200.  
  1201. #pragma segment Controls
  1202. void    CLUpdate(RgnHandle clipRgn, ListHandle listHndl)
  1203. {
  1204.     WindowPtr        thePort, listPort;
  1205.     short            txFont, txMode, txSize;
  1206.     Style            txFace;
  1207.     CLDataHndl        listData;
  1208.     ControlHandle    ctl;
  1209.  
  1210.     if (listHndl) {
  1211.  
  1212.         if (ctl = CLViewFromList(listHndl)) {
  1213.     
  1214.             GetPort(&thePort);
  1215.  
  1216.             txFont = thePort->txFont;
  1217.             txFace = thePort->txFace;
  1218.             txMode = thePort->txMode;
  1219.             txSize = thePort->txSize;
  1220.  
  1221.             listData = (CLDataHndl)(*ctl)->contrlData;
  1222.             TextFont((*listData)->txFont);
  1223.             TextFace((*listData)->txFace);
  1224.             TextMode((*listData)->txMode);
  1225.             TextSize((*listData)->txSize);
  1226.  
  1227.             listPort = (*listHndl)->port;
  1228.             (*listHndl)->port = thePort;
  1229.             LUpdate(clipRgn, listHndl);
  1230.             (*listHndl)->port = listPort;
  1231.  
  1232.             TextFont(txFont);
  1233.             TextFace(txFace);
  1234.             TextMode(txMode);
  1235.             TextSize(txSize);
  1236.         }
  1237.     }
  1238. }
  1239.  
  1240.  
  1241.  
  1242. static void    dummyCLUpdate(RgnHandle clipRgn, ListHandle listHndl)
  1243. {
  1244. #pragma unused (clipRgn, listHndl)
  1245. }
  1246.  
  1247.  
  1248.  
  1249. /*****************************************************************************/
  1250.  
  1251.  
  1252.  
  1253. /* Return the control handle for the view control that owns the List
  1254. ** record.  Use this to find the view to do customizations such as changing
  1255. ** the update procedure for this List control. */
  1256.  
  1257. #pragma segment Controls
  1258. ControlHandle    CLViewFromList(ListHandle listHndl)
  1259. {
  1260.     WindowPtr        window;
  1261.     ControlHandle    viewCtl;
  1262.     ListHandle        list;
  1263.  
  1264.     if (!listHndl) return(nil);
  1265.  
  1266.     window = (WindowPtr)(*listHndl)->port;
  1267.  
  1268.     for (viewCtl = nil;;) {
  1269.         viewCtl = CLNext(window, &list, viewCtl);
  1270.         if ((!viewCtl) || (list == listHndl)) return(viewCtl);
  1271.     }
  1272. }
  1273.  
  1274.  
  1275.  
  1276. static ControlHandle    dummyCLViewFromList(ListHandle listHndl)
  1277. {
  1278. #pragma unused (listHndl)
  1279.     return(nil);
  1280. }
  1281.  
  1282.  
  1283.  
  1284. /*****************************************************************************/
  1285.  
  1286.  
  1287.  
  1288. /* This window is becoming active or inactive.  The borders of the List
  1289. ** controls need to be redrawn due to this.  For each List control in the
  1290. ** window, redraw the active border. */
  1291.  
  1292. #pragma segment Controls
  1293. void    CLWindActivate(WindowPtr window)
  1294. {
  1295.     ControlHandle    viewCtl;
  1296.     ListHandle        list;
  1297.  
  1298.     if (window) {
  1299.  
  1300.         for (viewCtl = nil;;) {
  1301.             viewCtl = CLNext(window, &list, viewCtl);
  1302.             if (!viewCtl) break;
  1303.             CLBorderDraw(list);
  1304.         }
  1305.  
  1306.         lastKeyTime = 0;    /* Restart the entry collection for Lists. */
  1307.     }
  1308. }
  1309.  
  1310.  
  1311.  
  1312. static void    dummyCLWindActivate(WindowPtr window)
  1313. {
  1314. #pragma unused (window)
  1315. }
  1316.  
  1317.  
  1318.  
  1319. /*****************************************************************************/
  1320.  
  1321.  
  1322.  
  1323. #pragma segment Controls
  1324. pascal short MyIntlCompare(Ptr aPtr, Ptr bPtr, short aLen, short bLen)
  1325. {
  1326.     short    cmp;
  1327.  
  1328.     cmp = IUMagString(aPtr, bPtr, aLen, bLen);
  1329.     if (cmp == -1) return(1);
  1330.     else           return(0);
  1331. }
  1332.  
  1333.  
  1334.  
  1335.